/* 
 * File:   TestResults.cpp
 * Author: ungerma
 * 
 * Created on 11. August 2014, 11:55
 */

#include "TestResults.h"
#include "Images.h"

TestResults::TestResults() {
    
    aa  = 0;
    bb  = 0;
    ab  = 0; 
    ba  = 0;
    as  = 0;
    bs  = 0;
    right = 0;
    wrong = 0;
    
    // Sic!
    minY = Images::MAX_Y;
    maxY = Images::MIN_Y;
    
}

void TestResults::result(float y, float groundTruth, float classificationResult,
        string id) {
    
    ys.push_back(y);
    truths.push_back(groundTruth);
    minY = min(minY, y);
    maxY = max(maxY, y);
    
    if(classificationResult == CLASS_A) {
        if(groundTruth == classificationResult) {
            ++right;
            ++aa;
        }
        else {
            ++wrong;
            ++ab;
            wrongIds.push_back(id);
        }
    }
    else if(classificationResult == CLASS_B) {
        if(groundTruth == classificationResult) {
            ++right;
            ++bb;
        }
        else {
            ++wrong;
            ++ba;
            wrongIds.push_back(id);
        }
    }
    else {
        throw logic_error("Class label must be 1 or -1");
    }
    
    if(groundTruth == CLASS_A) {
        ++as;
    }
    else if(groundTruth == CLASS_B) {
        ++bs;
    }
    else {
        throw logic_error("Ground truth must be 1 or -1");
    }
    
}

float TestResults::getPrecision() {
    
    // How happy we are with what we have got.
    if((aa + ab) > 0) {
        return aa / (aa + ab);
    }
    return 0;
    
}

float TestResults::getRecall() {
    
    // How much more we could have had.
    if((aa + ba) > 0) {
        return aa / (aa + ba);
    }
    return 0;
    
}

void TestResults::summaryOutput() {
    
    int n = ys.size();
    int t = aa + bb;
    int f = ab + ba;
    float p = getPrecision();
    float r = getRecall();
    
    cout << "------------------------------------------------" << endl;
    cout << "Summary statistics" << endl;
    cout << "------------------------------------------------" << endl;
    cout << "Classified n=" << n << " samples" << endl;
    cout << "Number of as |a|=" << as << endl;
    cout << "Number of bs |b|=" << bs << endl;
    cout << "Classified as a (ground truth a) aa=" << aa << endl;
    cout << "Classified as a (ground truth b) ab=" << ab << endl;
    cout << "Classified as b (ground truth a) ba=" << ba << endl;
    cout << "Classified as b (ground truth b) bb=" << bb << endl;
    cout << "Correct classifications t=" << t << endl;
    cout << "Wrong classifications f=" << f << endl;
    cout << "Precision p=" << p << endl;
    cout << "Recall r=" << r << endl;
    cout << "------------------------------------------------" << endl;
            
}

void TestResults::trace() {
    
    cout << "Wrong classification details" << endl;
    cout << "------------------------------------------------" << endl;
    for(int i=0; i < wrongIds.size(); ++i) {
        cout << wrongIds.at(i) << endl;
    }
    cout << "------------------------------------------------" << endl;
    
}

void TestResults::precisionRecallCurve(string filename) {
    
    int noPoints = 0;
    float res;
    ofstream file;
    
    file.open(filename.c_str(), file.out);
    if(!file.is_open()) {
         throw runtime_error("Could not open " + filename + " for writing");
    }
    
    for(float boundary = PR_MIN; boundary <= PR_MAX; boundary += PR_STEP_SIZE) {
        
        TestResults results;
        for(int i=0; i<ys.size(); ++i) {
                        
            if(ys.at(i) > boundary) {
                res = CLASS_A;
            }
            else {
                res = CLASS_B;
            }
            results.result(ys.at(i), truths.at(i), res, "");
            
        }
        file << boundary << "," << results.getPrecision() << "," << 
                results.getRecall() << endl;
        ++noPoints;
    }
    
    file.close();
    if(file.is_open()) {
        throw runtime_error("Could not close " + filename);
    }
    cout << "Serialized " << noPoints << " PR data point(s) " << endl;
    
}

string TestResults::okString() {
        
    std::ostringstream ss;
    ss << (aa + bb);
    return ss.str();
    
}
